/********************************************************************/
/*                                                                  */
/*	       CREAM: A (JVM-like) Toy Abstract Machine             */
/*                                                                  */
/*		Copyright (c) 1998- , C.R. Ramakrishnan             */
/*                                                                  */
/*  $Id: inst.c,v 1.2 2012/04/26 18:19:32 cram Exp $ */
/*                                                                  */
/********************************************************************/

#include "machine.h"


/* First, some utility functions */

static union float_conv {
    float f;
    MemoryElement i;
} float_conv;

float i2f(MemoryElement i)
{
  float_conv.i = i;
  return float_conv.f;
}

MemoryElement f2i(float f)
{
  float_conv.f = f;
  return float_conv.i;
}


static int multianewarray_noclobber(int dimensions)
{ /* stack, size_1, size_2, ..., size_n => stack, size_1, size_2, ..., size_n */

  int j, handle, size;

  if (dimensions == 0)
    {
      return 0;
    }
  else
    {
      size = stack[sp-dimensions];
      handle = hp;
      hp += size;
      {
	for(j=0; j < size; j++)
	  heap[handle+j] = multianewarray_noclobber(dimensions-1);
      }
      return handle;
    }
}


/* CREAM INSTRUCTIONS: */

void ildc(int i)
     /* stack => stack, i */
{
  stack[sp++] = (MemoryElement) i;
}

void fldc(float f)
     /* stack => stack, f */
{
  stack[sp++] = f2i(f);
}

void null()
     /* stack => stack, null */
{
  stack[sp++] = 0;
}

void load()
     /* stack, locvar => stack, value */
{
  stack[sp-1] = stack[bp+stack[sp-1]];
}

void store()
     /* stack, locvar, value => value */
{ 
  stack[bp+stack[sp-2]] = stack[sp-1];
  stack[sp-2] = stack[sp-1];
  sp --;
}

void int2float()
     /* stack, value => stack, result */
{
  stack[sp-1] = f2i((float)stack[sp-1]);
}

void aload()
     /* stack, array, index => stack, value */
{ 

  int base = stack[sp-2]; /* get array handle */
  stack[sp-2] = heap[base+stack[sp-1]];  /* get array element */
  sp--;
}


void astore()
     /* stack, array, index, value => stack, value */
{ 

  int base = stack[sp-3]; /* get array handle */
  heap[base+stack[sp-2]] = stack[sp-1];  /* get array element */

  stack[sp-3] = stack[sp-1];
  sp -= 2;  

}


void newarray(int dimensions)
     /* stack, size_1, size_2, ..., size_n => stack, array_handle */
{ 

  int handle = multianewarray_noclobber(dimensions);

  sp -= dimensions;
  stack[sp++] = handle;
}

void nop()
     /* stack => stack */
{
}

void pop()
     /* stack, value => stack */
{
  sp--;
}

void dup()
     /* stack, value => stack, value, value */
{
  stack[sp] = stack[sp-1];
  sp++;
}

void dup2()
     /* stack, value1, value2 => stack, value1, value2, value1, value2 */
{
  stack[sp+1] = stack[sp-1];
  stack[sp] = stack[sp-2];
  sp+=2;
}

void swap()
     /* stack, value1, value2 => stack, value2, value1 */
{
  /* use area above sp as scratch */
  stack[sp] = stack[sp-1];
  stack[sp-1] = stack[sp-2];
  stack[sp-2] = stack[sp];
}

void swap1_2()
     /* stack, value1, value2, value3 => stack, value3, value1, value2 */
{
  /* exchange the top element of the stack with the next two elements */
  /* use area above sp as scratch */
  stack[sp] = stack[sp-1];
  stack[sp-1] = stack[sp-2];
  stack[sp-2] = stack[sp-3];
  stack[sp-3] = stack[sp];
}

void iadd()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (((int) stack[sp-2]) + ((int)stack[sp-1]));
  sp--;
}

void isub()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (((int) stack[sp-2]) - ((int)stack[sp-1]));
  sp--;
}

void imul()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (((int) stack[sp-2]) * ((int)stack[sp-1]));
  sp--;
}

void idiv()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (((int) stack[sp-2]) / ((int)stack[sp-1]));
  sp--;
}

void ineg()
     /* stack, value => stack, result */
{
  stack[sp-1] = (MemoryElement) (- ((int) stack[sp-1]));
}

void fadd()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = f2i(i2f(stack[sp-1]) + i2f(stack[sp-2]));
  sp--;
}

void fsub()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = f2i(i2f(stack[sp-2]) - i2f(stack[sp-1]));
  sp--;
}

void fmul()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = f2i(i2f(stack[sp-2]) * i2f(stack[sp-1]));
  sp--;
}

void fdiv()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = f2i(i2f(stack[sp-2]) / i2f(stack[sp-1]));
  sp--;
}

void fneg()
     /* stack, value => stack, result */
{
  stack[sp-1] = f2i(- i2f(stack[sp-1]));
}

void and()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (((int) stack[sp-2]) && ((int)stack[sp-1]));
  sp--;
}

void or()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (((int) stack[sp-2]) || ((int)stack[sp-1]));
  sp--;
}

void not()
     /* stack, value => stack, result */
{
  stack[sp-1] = (MemoryElement) (!((int) stack[sp-1]));
}

void iequal()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (((int) stack[sp-2]) == ((int)stack[sp-1]));
  sp--;
}

void inotequal()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (((int) stack[sp-2]) != ((int)stack[sp-1]));
  sp--;
}

void igte()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (((int) stack[sp-2]) >= ((int)stack[sp-1]));
  sp--;
}

void igt()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (((int) stack[sp-2]) > ((int)stack[sp-1]));
  sp--;
}

void ilte()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (((int) stack[sp-2]) <= ((int)stack[sp-1]));
  sp--;
}

void ilt()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (((int) stack[sp-2]) < ((int)stack[sp-1]));
  sp--;
}

void fequal()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (i2f(stack[sp-2]) == i2f(stack[sp-1]));
  sp--;
}

void fnotequal()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (i2f(stack[sp-2]) != i2f(stack[sp-1]));
  sp--;
}

void fgte()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (i2f(stack[sp-2]) >= i2f(stack[sp-1]));
  sp--;
}

void fgt()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (i2f(stack[sp-2]) > i2f(stack[sp-1]));
  sp--;
}

void flte()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (i2f(stack[sp-2]) <= i2f(stack[sp-1]));
  sp--;
}

void flt()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (i2f(stack[sp-2]) < i2f(stack[sp-1]));
  sp--;
}

void oequal()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (stack[sp-2] == stack[sp-1]);
  sp--;
}

void onotequal()
     /* stack, value1, value2 => stack, result */
{
  stack[sp-2] = (MemoryElement) (stack[sp-2] != stack[sp-1]);
  sp--;
}

/* Control instructions are defined in inst.h */

void new_env(int nargs, int nlocals)
     /* stack => stack, localvars, bookkeeping info */
{
  int target;

  target = sp + nlocals;  /* allocate space for local variables */
  for(; sp < target; sp++)
    stack[sp] = 0;

  stack[sp++] = bp;       /* save old base (vars) pointer */
  bp = sp - nargs - nlocals - 1;  /* set it to current beginning of vars */
  stack[sp++] = ep;     /* save environment pointer */
  ep = sp-1;              /* set e.p. to current environment */

  /* ready to begin loading fresh operands */
}

void rm_env()
     /* stack, localvars, bookkeeping info => stack */
{
  int retval = stack[sp-1]; /* save return value */
  sp = bp+1;                 /* top of old operand stack */
  bp = stack[ep-1];          /* restore vars  pointer */
  ep = stack[ep];        /* restore old value of environment pointer */
  stack[sp-1] = retval;

  /* everything is as though this call never took place */
}

void newobj(int size)
     /* stack => stack, handle */
{ 
                                      /* The actual new() used in Java
					 creates space given the class's
					 index into constant pool.
					 Once I figure out what the
					 constant pool is, I'll change this...
				      */
  int handle = hp;
  int n = size;
  
  hp += size;

  for(; n > 0; --n)                   /* initialize all fields */
    heap[hp-n] = 0;

  stack[sp++] = handle;

}

void putfield()
     /* stack, handle, offset, value => stack, value */
{ 

  int base = stack[sp-3];

  heap[base+stack[sp-2]] = stack[sp-1];

  stack[sp-3] = stack[sp-1];
  sp -= 2; 
}

void getfield()
     /* stack, handle, offset => stack, value */
{ 

  int base = stack[sp-2];

  stack[sp-2] = heap[base+stack[sp-1]];
  sp--;
}

void putstatic()
     /* stack, region-number, offset, value => stack, value */
{ 

  int base = region_table[stack[sp-3]];

  static_area[base+stack[sp-2]] = stack[sp-1];

  stack[sp-3] = stack[sp-1];
  sp -= 2; 
}

void getstatic()
     /* stack, region-number, offset  => stack, value */
{ 

  int base = region_table[stack[sp-2]];

  stack[sp-2] = static_area[base+stack[sp-1]];
  sp--;
}

void init_memory()
{
  sp = bp = hp = ep = 1;
  /* initialize array indices to 1, so that we can tell between
     unallocated and allocated instances */
}

#define Outfile stderr

int mysp, myep, mybp;

void print_stack()
{
  /* Print out entire stack for debugging */

  mysp = sp;
  myep = ep;
  mybp = bp;

  fprintf(Outfile, "      Stack pointer (sp) = %u\n", mysp);
  fprintf(Outfile, "Environment pointer (ep) = %u\n", myep);
  fprintf(Outfile, "       Base pointer (bp) = %u\n", mybp);

  fprintf(Outfile, "\nStack:\n\n");

  while(mysp > 1)
    {
      int i;

      fprintf(Outfile, " Operands:\n\n");
      for(i=mysp-1; i>myep; i--)
	fprintf(Outfile, "         %8u  %8lu\n", i, stack[i]);

      fprintf(Outfile, "\n Book-keeping:\n\n");
      fprintf(Outfile, " old_ep  %8u  %8lu\n", myep, stack[myep]);
      fprintf(Outfile, " old_bp  %8u  %8lu\n", myep-1, stack[myep-1]);

      fprintf(Outfile, "\n Locals:\n\n");
      for(i=myep-2; i>=mybp; i--)
	fprintf(Outfile, "         %8u  %8lu\n", i, stack[i]);

      mysp = mybp;
      mybp = stack[myep-1];
      myep = stack[myep];
    }
}

void print_current_frame()
{
  /* print out current stack frame, for debugging */

  int i;

  mysp = sp;
  myep = ep;
  mybp = bp;

  fprintf(Outfile, "      Stack pointer (sp) = %u\n", mysp);
  fprintf(Outfile, "Environment pointer (ep) = %u\n", myep);
  fprintf(Outfile, "       Base pointer (bp) = %u\n", mybp);

  fprintf(Outfile, "\nStack:\n\n");

  fprintf(Outfile, " Operands:\n\n");
  for(i=mysp-1; i>myep; i--)
    fprintf(Outfile, "         %8u  %8lu\n", i, stack[i]);
  
  fprintf(Outfile, "\n Book-keeping:\n\n");
  fprintf(Outfile, " old_ep  %8u  %8lu\n", myep, stack[myep]);
  fprintf(Outfile, " old_bp  %8u  %8lu\n", myep-1, stack[myep-1]);
  
  fprintf(Outfile, "\n Locals:\n\n");
  for(i=myep-2; i>=mybp; i--)
    fprintf(Outfile, "         %8u  %8lu\n", i, stack[i]);
}
